/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "arch/asm_support.h"

// void InterpreterToCompiledCodeBridgeDyn(
//       const BytecodeInstruction* insn,       x0
//       const Frame *iframe,                   x1
//       const Method*,                         x2
//       ManagedThread* thread)                 x3
.global InterpreterToCompiledCodeBridgeDyn
.type InterpreterToCompiledCodeBridgeDyn, %function
InterpreterToCompiledCodeBridgeDyn:
    CFI_STARTPROC
    CFI_DEF_CFA(sp, 0)

    stp x1, lr, [sp, -16]!
    CFI_ADJUST_CFA_OFFSET(2 * 8)
    CFI_REL_OFFSET(lr, 8)
    stp x19, THREAD_REG, [sp, -32]!
    CFI_ADJUST_CFA_OFFSET(4 * 8)
    CFI_REL_OFFSET(THREAD_REG, 8)
    CFI_REL_OFFSET(x19, 0)
    str fp, [sp, 16]
    CFI_REL_OFFSET(fp, 16)
    add fp, sp, 32
    CFI_DEF_CFA(fp, (2 * 8))
    mov THREAD_REG, x3

    // According to the current frame kind set the bridge type
    ldrb w3, [THREAD_REG, #MANAGED_THREAD_FRAME_KIND_OFFSET]
    tst w3, #0x1
    mov w4, #BYPASS_BRIDGE
    mov w5, #INTERPRETER_TO_COMPILED_CODE_BRIDGE
    csel w3, w4, w5, ne
    str x3, [sp, #24]
    // sp should be 16 byte aligned.

    // setup regs as follow
    // x9 - frame.vregs, x10 - insn_ptr, x12 - method, x15 - acc
    // x19 - frame, x13, x14 - temp
    add x9, x1, FRAME_VREGS_OFFSET
    ldr x15, [x1, #FRAME_ACC_OFFSET]
    mov x10, x0
    mov x12, x2
    mov x19, x1 // save frame to survive the call

    // the following dispatch code expects opcode in w5
    ldrb w5, [x10] // read opcode and advance insn_ptr
    cmp w5, #MIN_PREFIX_OPCODE_INDEX
    blt .Lnonprefixed
    ldrh w5, [x10], #2
    b .Ldispatch
.Lnonprefixed:
    add x10, x10, #1

.Ldispatch:
    // The file contains code which checks opcode and jumps
    // to the corresponding handler.
    // At the end each handler jumps to .Lload_reg_args label.
    // The file is autogenerated from runtime/templates/bridge_dispatch.S.erb
    // Handlers are distinguished by format and located in the corresponding files with name:
    // handle_call_<format>.S
    // If you get a compilation error that there is no such file it seems
    // new call format was introduced and you have to implement the corresponding handler.
#include "bridge_dispatch_dyn_aarch64.S"

    // invoke the method
    // since the first argument is Method* it must be in x0
.Linvoke:
    // invoke the entrypoint
    ldr lr, [x0, METHOD_COMPILED_ENTRY_POINT_OFFSET]
    blr lr
    // sp may be modified by call

    // handle the result
    // setup regs as follow:
    // x0 - result value, x19 - frame.acc
    add x19, x19, FRAME_ACC_OFFSET
    str x0, [x19]

    // Signal handler of the sampling profiler use stack space below sp, 
    // so change it carefully only after registers restoration
    sub sp, fp, 32
    ldp x19, THREAD_REG, [sp], #16
    CFI_RESTORE(THREAD_REG)
    CFI_RESTORE(x19)
    ldr lr, [sp, #24]
    CFI_RESTORE(lr)
    ldr fp, [sp], #32
    CFI_RESTORE(fp)
    CFI_DEF_CFA(sp, 0)
    ret
    CFI_ENDPROC

// void InvokeCompiledCodeWithArgArrayDyn(
//       const coretypes::TaggedValue* values,      x0
//       uint32_t num_args,                     x1
//       const Frame *iframe,                   x2
//       const Method*,                         x3
//       ManagedThread* thread)                 x4
.global InvokeCompiledCodeWithArgArrayDyn
.type InvokeCompiledCodeWithArgArrayDyn, %function
InvokeCompiledCodeWithArgArrayDyn:
    CFI_STARTPROC
    CFI_DEF_CFA(sp, 0)

    stp x2, lr, [sp, -16]!
    CFI_ADJUST_CFA_OFFSET(2 * 8)
    CFI_REL_OFFSET(lr, 8)
    stp x19, THREAD_REG, [sp, -32]!
    CFI_ADJUST_CFA_OFFSET(4 * 8)
    CFI_REL_OFFSET(THREAD_REG, 8)
    CFI_REL_OFFSET(x19, 0)
    str fp, [sp, 16]
    CFI_REL_OFFSET(fp, 16)
    add fp, sp, 32
    CFI_DEF_CFA(fp, (2 * 8))
    mov THREAD_REG, x4

    // According to the current frame kind set the bridge type
    ldrb w4, [THREAD_REG, #MANAGED_THREAD_FRAME_KIND_OFFSET]
    tst w4, #0x1
    mov w5, #BYPASS_BRIDGE
    mov w6, #INTERPRETER_TO_COMPILED_CODE_BRIDGE
    csel w4, w5, w6, ne
    str x4, [sp, 24]
    // sp should be 16 byte aligned.

    // setup regs as follow
    // x0       <- panda::Method*
    // x1       <- num_args (already on the relevant reg, no moves required)
    // x2..x7   <- user's code arguments, in boxed form
    // x9, w10  <- temps for moving user's args to ABI regs
    // x11,     <- temp for moving user's args to stack
    // lr       <- entrypoint
    ldr lr, [x3, #METHOD_COMPILED_ENTRY_POINT_OFFSET]
    mov x9, x0
    mov x0, x3

    // No arguments passed to the callee
    cbz w1, .Linvoke_

    // Reserve stack args
    sub x10, sp, x1, lsl 3
    and sp, x10, -16

    lsl w10, w1, 3
    sub w10, w10, 8
.Lloop_:
    ldr x11, [x9, x10]
    str x11, [sp, x10]
    subs w10, w10, 8
    bhs .Lloop_

.Linvoke_:
    // invoke the entrypoint
    blr lr
    
    // sp may be modified by call
    // Signal handler of the sampling profiler use stack space below sp, 
    // so change it carefully only after registers restoration
    sub sp, fp, 32
    ldp x19, THREAD_REG, [sp], #16
    CFI_RESTORE(THREAD_REG)
    CFI_RESTORE(x19)
    ldr lr, [sp, #24]
    CFI_RESTORE(lr)
    ldr fp, [sp], #32
    CFI_RESTORE(fp)
    CFI_DEF_CFA(sp, 0)
    ret
    CFI_ENDPROC
